package nodex

import (
	"bytes"
	"encoding/json"
	"gitee.com/zhongguo168a/go-nodex/nodex/server"
	"gitee.com/zhongguo168a/go-nodex/nodex/server/message"
	"gitee.com/zhongguo168a/gocodes/datax/cachex"
	"sync"
)

// key格式: objectID@tableName
type ISyncConn interface {
	Create(key string, data interface{})
	Delete(key string)
	Update(key string, data interface{})
	Event(name string, data interface{})
	Command(cmd string, args interface{})
	Now() ISyncConnNow
	Flush()
}

type ISyncConnNow interface {
	Create(key string, data interface{})
	Delete(key string)
	Update(key string, data interface{})
	Event(name string, data interface{})
	Command(cmd string, args interface{})
}

type OptKind int

const (
	OptKind_创建 = iota + 1
	OptKind_更新
	OptKind_删除
	OptKind_事件
)

type syncItem struct {
	//
	opt OptKind
	//
	data interface{}
	//
	key string
	//
	change map[string]interface{}
}

// 立即同步
func NewSyncConn(conn server.IConnection) (obj *SyncConnection) {
	obj = &SyncConnection{}
	obj.IConnection = conn
	obj.writerBuff = bytes.NewBuffer([]byte{})
	return
}

// SyncConnection 数据处理完毕，数据库已经更新完成，同步数据至客户端
// 如果同步到客户端的过程出现异常，导致客户端的数据与服务器的数据不再同步，服务器应该主动断开连接，促使客户端重新登录，获取最新数据
type SyncConnection struct {
	server.IConnection
	// 锁
	mutex sync.Mutex
	// 如果AutoFlush, 缓存同步数据
	syncItems []server.IMessage
	// 是否立即同步
	now bool
	//
	writerBuff *bytes.Buffer
	//
	SerializeMode SerializeMode
}

// Now 新建一个立即同步的实例
func (u *SyncConnection) Now() ISyncConnNow {
	n := NewSyncConn(u.IConnection)
	n.now = true
	return n
}

func (u *SyncConnection) Flush() {
	//u.mutex.Lock()
	//defer u.mutex.Unlock()
	//
	//if len(u.syncItems) == 0 {
	//	return
	//}
	//
	//wb := u.writerBuff
	//wb.Reset()
	//
	//for _, val := range u.syncItems {
	//	u.writeMsg(val)
	//}
	//// 因为world对ctx产生了引用, 这里暂时主动清理
	//u.syncItems = []server.IMessage{}
}

func (u *SyncConnection) Create(key string, data interface{}) {
	item := &message.SyncCreate{
		Key:           key,
		SerializeMode: int8(JSON),
		Data: func() (x []byte) {
			m := map[string]interface{}{}
			switch obj := data.(type) {
			case cachex.IObject:
				m = obj.ToMap()
			case map[string]interface{}:
				m = obj
			default:
				panic("create: not support type")
			}
			rbytes, marerr := json.Marshal(m)
			if marerr != nil {
				panic("create: json marshal")
			}
			x = rbytes
			return
		}(),
	}

	if u.now {
		u.writeMsg(item)
	} else {
		u.writeMsgBuff(item)
	}
}

func (u *SyncConnection) Update(key string, data interface{}) {
	item := &message.SyncUpdate{
		Key:           key,
		SerializeMode: int8(JSON),
		Data: func() (x []byte) {
			m := map[string]interface{}{}
			switch obj := data.(type) {
			case cachex.IObject:
				m = obj.ToMap()
			case map[string]interface{}:
				m = obj
			default:
				panic("update: not support type")
			}
			rbytes, marerr := json.Marshal(m)
			if marerr != nil {
				panic("update: json marshal")
			}
			x = rbytes
			return
		}(),
	}

	if u.now {
		u.writeMsg(item)
	} else {
		u.writeMsgBuff(item)
	}
}
func (u *SyncConnection) generateKey(name string, id string) string {
	if name != "" {
		return id + "@" + name
	}

	return id
}

func (u *SyncConnection) Delete(key string) {
	item := &message.SyncDelete{
		Key: key,
	}
	if u.now {
		u.writeMsg(item)
	} else {
		u.writeMsgBuff(item)
	}
}

func (u *SyncConnection) Event(name string, data interface{}) {
	item := &message.EventMessage{
		Key:           name,
		SerializeMode: int8(JSON),
		Data: func() (x []byte) {
			m := map[string]interface{}{}
			switch obj := data.(type) {
			case cachex.IObject:
				m = obj.ToMap()
			case map[string]interface{}:
				m = obj
			default:
				panic("update: not support type")
			}
			rbytes, marerr := json.Marshal(m)
			if marerr != nil {
				panic("update: json marshal")
			}
			x = rbytes
			return
		}(),
	}

	if u.now {
		u.writeMsg(item)
	} else {
		u.writeMsgBuff(item)
	}
}

func (u *SyncConnection) Command(cmd string, args interface{}) {
	item := &message.CommandMessage{
		Key:           cmd,
		SerializeMode: int8(JSON),
		Data: func() (x []byte) {
			m := map[string]interface{}{}
			switch obj := args.(type) {
			case cachex.IObject:
				m = obj.ToMap()
			case map[string]interface{}:
				m = obj
			default:
				panic("update: not support type")
			}
			rbytes, marerr := json.Marshal(m)
			if marerr != nil {
				panic("update: json marshal")
			}
			x = rbytes
			return
		}(),
	}

	if u.now {
		u.writeMsg(item)
	} else {
		u.writeMsgBuff(item)
	}
}

func (u *SyncConnection) writeMsg(msg server.IMessage) {
	u.IConnection.SendMsg(msg)
}

func (u *SyncConnection) writeMsgBuff(msg server.IMessage) {
	u.IConnection.SendMsgBuff(msg)
}
